home *** CD-ROM | disk | FTP | other *** search
- { DAYLIB.PLB #3.00 85-08-17 TURBO PASCAL CALENDAR-DATE PROCEDURES
-
- V03 L00 Turbo Pascal version cloned on 85-08-17 by Dennis
- E. Hamilton. This version has smoother operation than
- was available in the Small-C clone mother, DAYLIB.CL
- version #2.04.
-
- V02 L04 84-03-20 SC80 version updated by DEH to take advantage
- of special fixed-point division entry in SC80.CC #5.06.
- L00 derivation on 83-10-09 by DEH for use with SC80 Small-C.
-
- V01 L00 created by DEH on 83-10-05 as DATES.PLB, for use with
- JRT Pascal 3.0, switching ordinal range for use with DRI
- software date stamps
-
- V00 L00 original Pascal version created by Steve Brecher and
- distributed on the CompuServe Programmers SIG by memo
- dated 83-07-13.
-
- These routines represent dates from January 1, 1978 to September 17, 2067
- by the positive number of consecutive days since December 31, 1977. This is
- the same ordinal day number used by Digital Research CP/M software for date
- stamping. (The MSDOS ordinal date is obtained by a simple post-adjustment.
- Negative ordinal date values are also allowed for use as pre-1978 dates,
- being useful for computations using anniversaries and birth dates as far
- back as Friday, April 13, 1888.)
-
- Calendar dates of the form mo-day-year (with year in full 4-digit form)
- are carried in structures of type calday. Simply declare that type for
- your own variables, such as
- var mmddyy: calday;
- start: integer;
-
- initializing the data as appropriate to your application. E.g.,
-
- repeat
- CalDate(mmddyy, Today);
- (* establishing readln default values *)
- write(CON, 'Start Date (mo day year)? ');
- readln(CON, mmddyy.mo, mmddyy.da, mmddyy.year);
- if mmddyy.year < 77
- then mmddyy.year := mmddyy.year + 2000
- else if mmddyy.year < 100
- then mmddyy.year := mmddyy.year + 1900;
- until not BadDate(mmddyy);
- start := since77(mmddyy);
-
- Ordinal dates are easier to store and manipulate within programs and data
- wherever direct use by people can be avoided. Ordinal dates are perfect
- for sorting by date, for comparison of two dates, and for determining the
- exact number of days between any two dates. (These are so useful that
- larger systems, such as Common LISP, carry ordinal SECONDS from New Year's
- Eve, GMT, January 1, 1900. }
-
- type
-
- calday = record {Calendar date from 1978-01-01 to 2067-09-17. This
- structure can be used for other dates on the Gregorian
- calendar as well, but only the above are guaranteed to
- have unambiguous, positive ordinal date values. With
- the procedures here, dates 1888-04-13 to 1977-12-31 are
- represented by ordinals < 1, but the nineteenth-century
- dates are generally not usable if you translate to a
- different origin (such as 1980-01-01 for MSDOS or the
- turn of the century value for ArpaNet and Common LISP}
-
- year: integer {typically 1900 .. 2067 with 0 for unknown
- in some applications };
-
- mo: byte { 1 .. 12 with 0 for unknown in some
- applications. This sequence of fields
- should be maintained for use in sorting
- as typeless values when ordinal dates
- aren't convenient for that purpose};
-
- da: byte { 1 .. 31 with 0 for unknown in some
- applications. Note that validity of
- the precise combination of values is
- very easily checked with the technique
- in the BadDate function. }
-
- end {calday};
-
-
-
-
- function
-
- WeekDay(day: integer {since77 ordinal date})
-
- : integer {0 for Sunday, 1 for Monday, ..., 6 for Saturday};
-
- begin {day-of-week for a specified since77 ordinal date}
-
- while day < 1
- do day := day+32767;
- {Converting negative-range values properly}
-
- WeekDay := pred(day) mod 7;
-
- end {WeekDay};
-
- function
-
- since77(date: calday {to be converted} )
-
- :integer {ordinal date: days that date is past 1977-12-31.
- This CP/M time-stamp basis has 1888-04-13 = day -32768,
- 1977-12-31 = day 0
- 2067-09-17 = day +32767 };
- const
-
- CumDays: array [0 .. 24] of integer
- {days prior to first day of month for normal and leap years}
-
- = (0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
- 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 );
-
- var day: integer {intermediate accumulation};
-
- begin {since77}
-
- day := (date.year - 1978)*365 + ord(date.year < 1901);
- {determining the number of non-leap days from the end of date.year-1
- to the end of 1977, pre-compensating for 1900 being no leap year}
-
- if date.year < 1977
- then day := day + (date.year - 1980) div 4
- else day := day + (date.year - 1977) div 4;
- {figuring in number of leap days also passed over in that interval}
-
- if ((date.year and $3) = 0) and (date.year <> 1900)
- then date.mo := date.mo + 12;
- {adjusting for the current year itself being a leap-year (including 2000
- but not 1900) by using month numbers 13-24 instead of 1-12}
-
- since77 := day + CumDays[date.mo] + date.da;
- {including the days prior to the requested day in the specified
- year. This procedure lets da = 0 usefully compute the last day
- of the preceding month, a handy variation. }
-
-
- end {since77};
-
- procedure
-
- CalDate(var date: calday {returned as result};
- day: integer {since77 ordinal date});
-
- const
-
- CumDays: array [0 .. 24] of integer
- {days prior to first day of month for normal and leap years}
-
- = (0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
- 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 );
- {an intentional duplication to keep this data private}
-
- begin {CalDate conversion of a since77 ordinal date to its unique
- corresponding Gregorian calendar date}
-
- date.mo := 12; date.da := 31;
- {setup to first compute the last day of year preceding that containing
- the specified day number}
-
- if day < 0
- then date.year := Trunc((day-730.75-ord(day<-28488))/365.25) + 1978
- {adjusting early-year determination for the 1900 leap skip too}
- else date.year := Trunc((day+730.0)/365.25) + 1975;
- {determining the PREVIOUS calendar year with careful adjustment for
- leap days going into those previous years};
-
- day := day - since77(date);
- {accounting for days taken in preceding calendar years}
-
- date.year := succ(date.year);
- if ((date.year and $3) = 0) and (date.year <> 1900)
- then date.mo := 24;
- {using the leap-year block if needed. Otherwise,
- start from date.mo = 12 already established}
-
- while day <= CumDays[date.mo]
- do date.mo := pred(date.mo);
- {finding the month containing day in the current year}
-
- date.da := day - CumDays[date.mo];
- {figuring the number of days into the correct month}
-
- if date.mo > 12
- then date.mo := date.mo - 12;
- {dropping the special leap-year month numbering}
-
- end {CalDate};
-
- function
-
- BadDate(date: calday {mo-da-year to be checked})
-
- :Boolean {for invalid calday, one not being for a unique
- ordinal date in the supported range};
-
- var check: calday {intermediate value for comparison};
-
- begin {Screening of the given date for proper value};
-
- CalDate(check, since77(date));
- {finds the only supported date, if any, for the ordinal date produced by
- since77 for the given date. The given date is valid here only if the
- check value is identical. }
-
- BadDate := (check.year <> date.year)
- or (check.mo <> date.mo )
- or (check.da <> date.da );
-
- end {BadDate};
-
-
- (* end of DAYLIB.PLB *)